using System;
using System.Collections.Generic;
using Fuse8.DomainFramework.Common.Locator;
using Fuse8.DomainFramework.DataAccess;
using Fuse8.DomainFramework.DataAccess.SqlDatabase;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Practices.DataAccess.SqlRepository;
using Fuse8.DomainFramework.Practices.Domain.Interfaces;
using Fuse8.DomainFramework.Practices.SystemFacade;
using Fuse8.DomainFramework.Practices.SystemFacade.Extensions;

namespace Fuse8.DomainFramework.Practices.DataAccess.Commands
{
    internal class GenericListSqlCommand<TEntity, TKey, TParameter> : SqlBaseCommand<GenericList<TEntity, TKey>, TParameter, TKey> where TKey : IComparable<TKey>, IEquatable<TKey> where TParameter : ICommandParameter where TEntity : class, IEntityObject<TKey>, new()
    {
        private static readonly Dictionary<string, bool> _mapperExists = new Dictionary<string, bool>();

        public GenericListSqlCommand()
        {
            var key = string.Format("{0}_{1}", typeof (TEntity), typeof (TParameter));
            if (!_mapperExists.ContainsKey(key))
            {
                InitMapper();
                _mapperExists[key] = true;
            }
        }        
        
        static GenericListSqlCommand()
        {
            InitRepository();

            InitMapper();
        }

        private static void InitMapper()
        {
            var entityMapper =
                DependencyServiceLocator.Current.GetInstance<ISqlEntityDataMapper<TEntity, TKey>>() as
                SqlBaseMapper<TEntity, TKey>;

            if (entityMapper != null)
            {
                string sp = CommandNameFacade.GetCommandName<TEntity, TParameter>();

                GenericListSqlMapper<TEntity, TKey> mapper = null;
                bool mapperExists = false;
                var setting = new SqlMultiMapperSettings<GenericList<TEntity, TKey>>();
                if (DependencyServiceLocator.Current.IsRegistered<ISqlEntityDataMapper<GenericList<TEntity, TKey>, TKey>>())
                {
                    mapper =
                        DependencyServiceLocator.Current.GetInstance
                            <ISqlEntityDataMapper<GenericList<TEntity, TKey>, TKey>>() as
                        GenericListSqlMapper<TEntity, TKey>;
                    mapperExists = true;
                    setting = mapper.Settings;
                }
                                                               
                setting.AddProperties<TParameter>(
                    c => new {List = c.MultiMap<TEntity, GenericList<TEntity, TKey>>(p => p.List, (p, t) => true)});

                int i = 1;
                var mappingProperty = entityMapper.Settings.GetProperty(sp, i);
                while (mappingProperty != null)
                {
                    setting.SetProperty(sp, i, mappingProperty);
                    mappingProperty = entityMapper.Settings.GetProperty(sp, ++i);
                }

                if (!mapperExists)
                {
                    mapper = new GenericListSqlMapper<TEntity, TKey>(setting);
                }
                
                DependencyServiceLocator.Current.Register<ISqlEntityDataMapper<GenericList<TEntity, TKey>, TKey>>(mapper);
            }
        }

        private static void InitRepository()
        {
			var connectionString =  DependencyServiceLocator.Current.GetInstance<IConnectionStringFactory>().GetConnectionString<GenericList<TEntity, TKey>>();
			var factory = SqlEntityDataRepositoryConfiguration<GenericList<TEntity, TKey>, TKey>.CreateUnitOfWorkFactory(connectionString);
            if (DependencyServiceLocator.Current.IsRegistered(typeof(IUnitOfWorkFactory<GenericList<TEntity, TKey>>)) || DependencyServiceLocator.Current.IsRegistered(typeof(IUnitOfWorkFactory<>)))
			{
                factory = DependencyServiceLocator.Current.GetInstance<IUnitOfWorkFactory<GenericList<TEntity, TKey>>>();
			}
            DependencyServiceLocator.Current.SetDefaultService(factory);
        }

        public override string CommandName
        {
            get
            {
                return CommandNameFacade.GetCommandName<TEntity, TParameter>();
            }
        }
    }
}